#include <errno.h>

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

#include <sys/shm.h>

#include <stdio.h>
#include <fcntl.h>


#define BUF_SIZE 1024

#define EMPTY 2
#define FULL  3

#define IN_BLOCK  0
#define OUT_BLOCK 1


int main( int argc, char **argv)
{
	if ( argc != 1)
	{
		printf("Wrong arguments\n");
		exit(0);
	}
	
	int fd =  open( ".for_keys", O_CREAT | 0644);
	if ( fd != -1)
		close(fd);
///////////////////////////////////////////////
	key_t key_sem_common  = ftok( ".for_keys", 10);
	key_t key_shm_common  = ftok( ".for_keys", 20);
///////////
	int sem_id_common = semget( key_sem_common ,4,IPC_CREAT | 0644);
	int shm_id_common = shmget( key_shm_common, BUF_SIZE + sizeof(int), IPC_CREAT |  0644);	
///////////////
	void  *ptr_shm = shmat( shm_id_common, NULL, NULL);
	char  *ptr_shm_buffer = (char* )ptr_shm + sizeof(int);
	int   *ptr_shm_count  = (int*  )ptr_shm    ;
////////////////////////////////////////////////		
	int fd_out = 1; //stdout
///////
	struct sembuf sem_buf[10];
		sem_buf[0].sem_num = OUT_BLOCK;    sem_buf[0].sem_op =  0;  sem_buf[0].sem_flg = NULL;
		sem_buf[1].sem_num = OUT_BLOCK;    sem_buf[1].sem_op =  1;  sem_buf[1].sem_flg = SEM_UNDO;
//		sem_buf[2].sem_num = IN_BLOCK;     sem_buf[2].sem_op =  1;  sem_buf[2].sem_flg = SEM_UNDO;
		sem_buf[2].sem_num = EMPTY;        sem_buf[2].sem_op =  3;  sem_buf[2].sem_flg = NULL;
		sem_buf[3].sem_num = EMPTY;        sem_buf[3].sem_op = -3;  sem_buf[3].sem_flg = SEM_UNDO;
		sem_buf[4].sem_num = EMPTY;        sem_buf[4].sem_op =  2;  sem_buf[4].sem_flg = NULL;
		
	semop ( sem_id_common, sem_buf,5);
	do
	{
		sem_buf[0].sem_num = FULL;         sem_buf[0].sem_op = -2;  sem_buf[0].sem_flg = NULL;
		semop( sem_id_common, sem_buf,1);
			if ( semctl( sem_id_common, FULL, GETVAL) != 0)
			{
				printf("Reader is killed\n");
				sem_buf[0].sem_num = FULL;    sem_buf[0].sem_op =  -1;  sem_buf[0].sem_flg = NULL;
				semop( sem_id_common, sem_buf, 1);
				
				
				sem_buf[0].sem_num = EMPTY;        sem_buf[0].sem_op =  3;  sem_buf[0].sem_flg = SEM_UNDO;
				sem_buf[1].sem_num = EMPTY;        sem_buf[1].sem_op = -3;  sem_buf[1].sem_flg = NULL;
				
				semop( sem_id_common, sem_buf , 2);	
				
				
				exit(0);
			}
	
			int write_count = 0;
			do
			{
				int tmp = write( fd_out, ptr_shm_buffer + write_count, *ptr_shm_count - write_count);
				if ( tmp == -1)
				{
					if ( errno == EINTR)
					{
						continue;
					}else
					{
						perror("Out write error");
						exit(0); 
					}
				}
				else
				{
					write_count +=tmp;
				}
			}
			while ( write_count != *ptr_shm_count);
		if ( *ptr_shm_count != 0 )
		{
			sem_buf[0].sem_num = EMPTY;         sem_buf[0].sem_op =  2;  sem_buf[0].sem_flg = NULL;
			semop( sem_id_common, sem_buf,1);
		}
	}
	while( *ptr_shm_count != 0);
//////////////////////////////////////
	sem_buf[0].sem_num = EMPTY;        sem_buf[0].sem_op =  3;  sem_buf[0].sem_flg = SEM_UNDO;
	sem_buf[1].sem_num = EMPTY;        sem_buf[1].sem_op = -3;  sem_buf[1].sem_flg = NULL;
	
	semop( sem_id_common, sem_buf , 2);	
}
